package com.data2wisdom.comm.extend.handler.impl;

import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.management.RuntimeErrorException;
import javax.persistence.Id;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.data2wisdom.comm.extend.dao.CommonParamMapper;
import com.data2wisdom.comm.extend.handler.IDynParamsHandler;
import com.data2wisdom.comm.extend.model.Param;
import com.data2wisdom.comm.extend.util.DateToStringConverter;
import com.data2wisdom.comm.extend.util.HandleAttributeUtil;
import com.data2wisdom.comm.extend.util.annotation.Solomon;
import com.data2wisdom.comm.extend.util.exception.BeanISNullException;
import com.data2wisdom.comm.extend.util.exception.IDNotFoundException;
import com.google.gson.Gson;


@Service
public class DynParamsHandler implements IDynParamsHandler {
	private Logger logger = Logger.getLogger(getClass());
	
	private static final String tableName = "D2W_UTIL_PARAMS";

	private static final String T = null;
	
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Autowired
	private CommonParamMapper paramDao;
	

	@Override
	public String getParamBeanType() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String setParamBeanType() {
		// TODO Auto-generated method stub
		return null;
	}
	@Transactional
	@Override
	public <T> void saveParamBean(T bean) throws Exception{
		//获取bean中哪些属性是需要存储扩展表的
		if(bean == null){
			throw new BeanISNullException("bean为空");
		}
		Class clazz = bean.getClass();
        //抽取公共属性,【父类中需要有一个公共属性id】
        String id = null;
		Field [] superFields = clazz.getSuperclass().getDeclaredFields();
		for(Field field :superFields){
			field.setAccessible(true);
			 if(field.isAnnotationPresent(Id.class)){
				 id =  String.valueOf(field.get(bean));
			 }
		 }
        //对象不存在id，返回，不处理
        if(id == null){
        	throw new IDNotFoundException("没有找到指定的id属性");
        }
        Field [] fields = clazz.getDeclaredFields();
        List<Param> params = new ArrayList<Param>();
        for(Field field :fields){
        	field.setAccessible(true);
            if(field.isAnnotationPresent(Solomon.class)){
            	Solomon solo = (Solomon) field.getAnnotation(Solomon.class);
            	//需要存储
            	if(solo.isDynParam()){
            		Param param = new Param(); 
            		param.setName(field.getName());
            		//先处理简单类型的值,后面再加上复杂的属性类型,【注意：简单类型的值都丢在value中，如果有byte则丢在blob中。】
            		if(field.getType() == String.class || 
            				field.getType() == Integer.class || field.getType() == Double.class 
            				|| field.getType() == Float.class){
            			try {
							param.setValue(String.valueOf(field.get(bean)));
						} catch (Exception e) {
							logger.error("从bean中读取属性值失败", e);
						}
            		}
            		if(field.getType() == Date.class){
            			try {
							param.setValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(field.get(bean)));
						} catch (Exception e) {
							logger.error("从bean中读取日期属性值失败", e);
            		    }
            	    }
            		if(field.getType() == Collection.class){
            			
            		}
            	   param.setNodeId(id);
            	   param.setParamType(clazz.getName());
            	   param.setRn(0); //目前给0，表示只有一个值，后面如果是list，则需要修改这段代码
            	   param.setIsBigData(0); //给普通，后面修改
            	   params.add(param);
            	}
            }
        }
        
        
        PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(bean);
		for (int i = 0; i < pds.length; i++) {
			Method getter = pds[i].getReadMethod();
	        Method setter = pds[i].getWriteMethod();
	        Class type = pds[i].getPropertyType();
			String key = pds[i].getName();
			Object value;
			if ("class".equals(key)) {
				continue;
			}
			if(setter == null) {
				continue;
			}
			if(setter == null){
				continue;
			}
			if(setter.isAnnotationPresent(Solomon.class)){
				Solomon solo = (Solomon)getter.getAnnotation(Solomon.class);
				if(solo.isDynParam()){
					try {
						value = PropertyUtils.getProperty(bean, key);
						if (Collection.class.isAssignableFrom(type)) {
							try {
								Collection ct = (Collection) value;
								Iterator iterator = ct.iterator();
								int ii = 0;
								while (iterator.hasNext()) {
									Object obj = iterator.next();
									Param param = new Param(); 
				            		param.setName(key);
				            		param.setNodeId(id);
				              	    param.setParamType(type.getName());
				              	    param.setRn(ii); 
				              	    param.setIsBigData(0);
				              	    param.setValue(obj.toString());
				              	    params.add(param);
									ii++;
								}
							} catch (IllegalArgumentException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						} else if (Map.class.isAssignableFrom(type)) {
							try {
								Map ct = (Map) value;
								Iterator iterator = ct.keySet().iterator();
//								while (iterator.hasNext()) {
//									Object ky = iterator.next();
//									Object val = ct.get(ky);
//								}
								Param param = new Param(); 
			            		param.setName(key);
			            		param.setNodeId(id);
			              	    param.setParamType(type.getName());
			              	    param.setRn(0); 
			              	    param.setIsBigData(0);
			              	    Gson gson = new Gson();
			              	    param.setValue(gson.toJson(ct));
			              	    params.add(param);
							} catch (IllegalArgumentException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							
						} else if (value instanceof byte[] || value instanceof Byte[]) {
							Param param = new Param(); 
		            		param.setName(key);
		            		param.setNodeId(id);
		              	    param.setParamType(type.getName());
		              	    param.setRn(0); 
		              	    param.setIsBigData(1);
							PropertyUtils.setProperty(param, "bigData", value);
							params.add(param);
						} else {
							DefaultConversionService conversionService = new DefaultConversionService();
							conversionService.addConverter(new DateToStringConverter());
							try {
								String val = conversionService.convert(value,
										String.class);
								System.err.println(val);
							} catch (IllegalArgumentException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} catch (Exception e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				
				
			}
		}
        //将封装好的参数存储数据库
        if(params != null && params.size() > 0){
        	batchAdd2Db(params);
        }
	}
	/**
	 * 私有方法：批量插入参数数据
	 * @param params
	 */
	private void batchAdd2Db(List<Param> params){
		final List<Param> local_params = params;
		StringBuilder sql_in = new StringBuilder();
		sql_in.append("insert into ").append(tableName).append(
				"(NODE_ID,NAME,VALUE,RN,PARAM_TYPE,BIGDATA,ISBIGDATA,CLOBDATA) values(?,?,?,?,?,?,?,?)");
		this.jdbcTemplate.batchUpdate(sql_in.toString(), new BatchPreparedStatementSetter(){
            public void setValues(PreparedStatement ps,int i)throws SQLException
               {
            	ps.setString(1, local_params.get(i).getNodeId());
            	ps.setString(2, local_params.get(i).getName());
            	ps.setInt(4, local_params.get(i).getRn());
            	ps.setString(5, local_params.get(i).getParamType());
            	ps.setInt(7, local_params.get(i).getIsBigData());
            	if(local_params.get(i).getIsBigData() == 2){//大文本类型
            		StringReader reader = new StringReader(local_params.get(i).getValue());
            		ps.setClob(8, reader);
            		ps.setNull(3, java.sql.Types.VARCHAR);
            		ps.setNull(6, java.sql.Types.BLOB);
				}else if(local_params.get(i).getIsBigData() == 1){ //大二进制类型
					byte[] buf = null;
					ByteArrayOutputStream bo = null;
					ObjectOutputStream oo = null;
					
					try {
						bo = new ByteArrayOutputStream();
						oo = new ObjectOutputStream(bo);
						oo.writeObject(local_params.get(i).getValue());
						buf = bo.toByteArray();		
						ps.setNull(3, java.sql.Types.VARCHAR);
	            		ps.setBlob(6, new java.io.ByteArrayInputStream(buf));
	            		ps.setNull(8, java.sql.Types.CLOB);
					} catch (Exception e) {
						logger.error("处理流失败", e);
					} finally {
						if (bo != null)
							try {
								bo.close();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						if (oo != null)
							try {
								oo.close();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
					}
				}else{//普通类型
					ps.setString(3, local_params.get(i).getValue());
            		ps.setNull(6, java.sql.Types.BLOB);
            		ps.setNull(8, java.sql.Types.CLOB);
				}
               }
               public int getBatchSize()
               {
                return local_params.size();
               }
        });
	}

	@Override
	public <T> T getParamBean(String objectId, T bean) {
		//从数据库获取数据，填充bean对象的通过Java注解标出要保存的变量,bean原有其他变量值不能丢。
		Map<String, Object> param = new HashMap<String,Object>();
		param.put("nodeId", objectId);
		param.put("paramType", bean.getClass().getName());
		List<Param> params = paramDao.findByParam(param);
		//封装已有对象
		return HandleAttributeUtil.convertBean(params, bean);
	}
	
	@Override
	public <T> T getParamBean(String objectId, Class<T> clazz) {
		Map<String, Object> param = new HashMap<String,Object>();
		param.put("nodeId", objectId);
		param.put("paramType", clazz.getName());
		List<Param> params = paramDao.findByParam(param);
		T obj = null;
		try {
			obj = clazz.newInstance();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return HandleAttributeUtil.convertBean(params, obj);
	}
	@Transactional
	@Override
	public <T> void deleteParamBean(String objectId, T bean) {
		Map<String, Object> param = new HashMap<String,Object>();
		param.put("nodeId", objectId);
		if(bean != null) {
			param.put("paramType", bean.getClass().getName());
		}
		paramDao.deleteByParam(param);
	}
	
	@Override
	public void saveParam(String objectid, String paramName, Object paramValue,Class<?> clazz) {
		
	}

	@Override
	public Object getParam(String objectId, String paramName) {
		// TODO Auto-generated method stub
		return null;
	}
	@Transactional
	@Override
	public <T> void updateParamBean(T bean) throws IDNotFoundException{
		//获取bean中哪些属性是需要存储扩展表的
				if(bean == null){
					throw new NullPointerException("bean为空");
				}
				Class clazz = bean.getClass();
		        //抽取公共属性,【父类中需要有一个公共属性id】
		        String id = null;
				Field [] superFields = clazz.getSuperclass().getDeclaredFields();
				for(Field field :superFields){
					field.setAccessible(true);
					 if(field.isAnnotationPresent(Id.class)){
						 try {
							id =  String.valueOf(field.get(bean));
						} catch (IllegalArgumentException | IllegalAccessException e) {
							throw new IDNotFoundException("没有找到指定的id属性");
						}
					 }
				 }
		        //对象不存在id，返回，不处理
		        if(id == null){
		        	throw new IDNotFoundException("没有找到指定的id属性");
		        }
		        Field [] fields = clazz.getDeclaredFields();
		        List<Param> params = new ArrayList<Param>();
		        for(Field field :fields){
		        	field.setAccessible(true);
		            if(field.isAnnotationPresent(Solomon.class)){
		            	Solomon solo = (Solomon) field.getAnnotation(Solomon.class);
		            	//需要存储
		            	if(solo.isDynParam()){
		            		Param param = new Param(); 
		            		param.setName(field.getName());
		            		//先处理简单类型的值,后面再加上复杂的属性类型,【注意：简单类型的值都丢在value中，如果有byte则丢在blob中。】
		            		if(field.getType() == String.class || 
		            				field.getType() == Integer.class || field.getType() == Double.class 
		            				|| field.getType() == Float.class){
		            			try {
									param.setValue(String.valueOf(field.get(bean)));
								} catch (Exception e) {
									logger.error("从bean中读取属性值失败", e);
								}
		            		}
		            		if(field.getType() == Date.class){
		            			try {
									param.setValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(field.get(bean)));
								} catch (Exception e) {
									logger.error("从bean中读取日期属性值失败", e);
		            		    }
		            	    }
		            		if(field.getType() == Collection.class){
		            			
		            		}
		            	   param.setNodeId(id);
		            	   param.setParamType(clazz.getName());
		            	   param.setRn(0); //目前给0，表示只有一个值，后面如果是list，则需要修改这段代码
		            	   param.setIsBigData(0); //给普通，后面修改
		            	   params.add(param);
		            	}
		            }
		        }
		        
		        
		        PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(bean);
				for (int i = 0; i < pds.length; i++) {
					Method getter = pds[i].getReadMethod();
			        Method setter = pds[i].getWriteMethod();
			        Class type = pds[i].getPropertyType();
					String key = pds[i].getName();
					Object value;
					if ("class".equals(key)) {
						continue;
					}
					if(setter == null) {
						continue;
					}
					if(setter.isAnnotationPresent(Solomon.class)){
						Solomon solo = (Solomon)getter.getAnnotation(Solomon.class);
						if(solo.isDynParam()){
							try {
								value = PropertyUtils.getProperty(bean, key);
								if (Collection.class.isAssignableFrom(type)) {
									try {
										Collection ct = (Collection) value;
										Iterator iterator = ct.iterator();
										int ii = 0;
										while (iterator.hasNext()) {
											Object obj = iterator.next();
											Param param = new Param(); 
						            		param.setName(key);
						            		param.setNodeId(id);
						              	    param.setParamType(type.getName());
						              	    param.setRn(ii); 
						              	    param.setIsBigData(0);
						              	    param.setValue(obj.toString());
						              	    params.add(param);
											ii++;
										}
									} catch (IllegalArgumentException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									} catch (Exception e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
								} else if (Map.class.isAssignableFrom(type)) {
									try {
										Map ct = (Map) value;
										Iterator iterator = ct.keySet().iterator();
//										while (iterator.hasNext()) {
//											Object ky = iterator.next();
//											Object val = ct.get(ky);
//										}
										Param param = new Param(); 
					            		param.setName(key);
					            		param.setNodeId(id);
					              	    param.setParamType(type.getName());
					              	    param.setRn(0); 
					              	    param.setIsBigData(0);
					              	    Gson gson = new Gson();
					              	    param.setValue(gson.toJson(ct));
					              	    params.add(param);
									} catch (IllegalArgumentException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									} catch (Exception e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
									
								} else if (value instanceof byte[] || value instanceof Byte[]) {
									Param param = new Param(); 
				            		param.setName(key);
				            		param.setNodeId(id);
				              	    param.setParamType(type.getName());
				              	    param.setRn(0); 
				              	    param.setIsBigData(1);
									PropertyUtils.setProperty(param, "bigData", value);
									params.add(param);
								} else {
									DefaultConversionService conversionService = new DefaultConversionService();
									conversionService.addConverter(new DateToStringConverter());
									try {
										String val = conversionService.convert(value,
												String.class);
										System.err.println(val);
									} catch (IllegalArgumentException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									} catch (Exception e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
								}
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
						
						
					}
				}
	        //将封装好的参数存储数据库
	        if(params != null && params.size() > 0){
	        	batchUpdate2Db(params);
	        }
	}

	private void batchUpdate2Db(List<Param> params) {
		final List<Param> local_params = params;
		StringBuilder sql_in = new StringBuilder();
		//sql_in.append("insert into ").append(tableName).append(
				//"(NODE_ID,NAME,VALUE,RN,PARAM_TYPE,BIGDATA,ISBIGDATA,CLOBDATA) values(?,?,?,?,?,?,?,?)");
		sql_in.append("update ").append(tableName).append(" set  VALUE = ?, BIGDATA = ?,ISBIGDATA = ?,RN = ?, CLOBDATA = ? where NODE_ID = ? and PARAM_TYPE = ? and NAME = ?");
		this.jdbcTemplate.batchUpdate(sql_in.toString(), new BatchPreparedStatementSetter(){
            public void setValues(PreparedStatement ps,int i)throws SQLException
            {
            	ps.setString(6, local_params.get(i).getNodeId());
            	ps.setString(8, local_params.get(i).getName());
            	ps.setInt(4, local_params.get(i).getRn());
            	ps.setString(7, local_params.get(i).getParamType());
            	ps.setInt(3, local_params.get(i).getIsBigData());
            	if(local_params.get(i).getIsBigData() == 2){//大文本类型
            		StringReader reader = new StringReader(local_params.get(i).getValue());
            		ps.setClob(8, reader);
            		ps.setNull(1, java.sql.Types.VARCHAR);
            		ps.setNull(2, java.sql.Types.BLOB);
				}else if(local_params.get(i).getIsBigData() == 1){ //大二进制类型
					byte[] buf = null;
					ByteArrayOutputStream bo = null;
					ObjectOutputStream oo = null;
					
					try {
						bo = new ByteArrayOutputStream();
						oo = new ObjectOutputStream(bo);
						oo.writeObject(local_params.get(i).getValue());
						buf = bo.toByteArray();		
						ps.setNull(1, java.sql.Types.VARCHAR);
	            		ps.setBlob(2, new java.io.ByteArrayInputStream(buf));
	            		ps.setNull(5, java.sql.Types.CLOB);
					} catch (Exception e) {
						logger.error("处理流失败", e);
					} finally {
						if (bo != null)
							try {
								bo.close();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						if (oo != null)
							try {
								oo.close();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
					}
				}else{//普通类型
					ps.setString(1, local_params.get(i).getValue());
            		ps.setNull(2, java.sql.Types.BLOB);
            		ps.setNull(5, java.sql.Types.CLOB);
				}
               }
               public int getBatchSize()
               {
                return local_params.size();
               }
        });
		
	}

	@Override
	public <T> List<T> getParamBeans(String objectId, T bean) {
		 List<T> list = new ArrayList<>();
		//从数据库获取数据，填充bean对象的通过Java注解标出要保存的变量,bean原有其他变量值不能丢。
			Map<String, Object> param = new HashMap<String,Object>();
			param.put("nodeId", objectId);
			param.put("paramType", bean.getClass().getName());
			List<Param> params = paramDao.findByParam(param);
			//封装已有对象
			Map<String,List<Param>> pMap = new HashMap<>();
			for (Param p : params) {
				String nodeId =  p.getNodeId();
				if(pMap.get(nodeId) == null) {
					List<Param> pl = new ArrayList<>();
					pl.add( p);
					pMap.put(nodeId, pl);
				}else {
					pMap.get(nodeId).add( p);
				}
			}
			for (Entry<String, List<Param>> entry : pMap.entrySet()) {
				T newInstance = null;
				try {
					newInstance = (T) bean.getClass().newInstance();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				T t = HandleAttributeUtil.convertBean(entry.getValue(),  newInstance);
				list.add(t);
			}
		return list;
	}
}
